package com.sbtr.security.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisServer;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
import java.util.List;

@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig2 {

	private final RedisProperties properties;

	public RedisConfig2(RedisProperties properties) {
		this.properties = properties;
	}
//
	@Bean
	public RedisTemplate<String, Object> redisTemplate() {

		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
		redisTemplate.setConnectionFactory(getConnectionFactory());

		// 用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
		StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
		redisTemplate.setKeySerializer(stringRedisSerializer); // key
		Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
		ObjectMapper objectMapper = new ObjectMapper();
		// 指定要序列化的域(field,get,set)，访问修饰符(public,private,protected)
		objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

		redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); //value

		redisTemplate.setHashKeySerializer(stringRedisSerializer);
		redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

		redisTemplate.afterPropertiesSet();
		return redisTemplate;
	}

	@Bean
	public RedisConnectionFactory getConnectionFactory() {
		//哨兵模式
		RedisSentinelConfiguration configuration = new RedisSentinelConfiguration();
		configuration.setMaster(properties.getSentinel().getMaster());
		configuration.setPassword(properties.getPassword());
		configuration.setDatabase(properties.getDatabase());
		List<String> nodes = properties.getSentinel().getNodes();
		nodes.forEach(node -> {
			String[] str = node.split(":");
			RedisNode redisServer = new RedisServer(str[0], Integer.parseInt(str[1]));
			configuration.sentinel(redisServer);
		});
		LettuceConnectionFactory factory = new LettuceConnectionFactory(configuration, getPool());
		//使用前先校验连接,这个最好是要配置：不然会带来connection reset by peer
		factory.setValidateConnection(true);
		return factory;
	}

	@Bean
	public LettuceClientConfiguration getPool() {
		GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
		//redis客户端配置:超时时间默认
		LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder
				builder = LettucePoolingClientConfiguration.builder().
				commandTimeout(Duration.ofMillis(60000));
		//连接池配置
		RedisProperties.Pool pool = properties.getLettuce().getPool();
		genericObjectPoolConfig.setMaxIdle(pool.getMaxIdle());
		genericObjectPoolConfig.setMinIdle(pool.getMinIdle());
		genericObjectPoolConfig.setMaxTotal(pool.getMaxActive());
		genericObjectPoolConfig.setMaxWaitMillis(pool.getMaxWait().toMillis());
		builder.shutdownTimeout(Duration.ofMillis(4000));
		builder.poolConfig(genericObjectPoolConfig);
		return builder.build();
	}
}